/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 adventure_object.cpp

	$Header: /game/adventure_object.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include <vector>
#include "adventure_object.h"

#include "adaptor_handler.h"
#include "adv_object_icon_type.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_path_finder.h"
#include "adventure_sounds.h"
#include "army.h"
#include "army_array.h"
#include "bound_handler.h"
#include "direction.h"
#include "enum_operations.h"
#include "footprint.h"
#include "function_handler.h"
#include "map_rect.h"
#include "map_renderer.h"
#include "map_segment_overlay.h"
#include "mini_map_window.h"
#include "options.h"
#include "pathfinding_subject.h"
#include "pathfinding_subject_ptr.h"
#include "pixel_24.h"
#include "sized_int_types.h"
#include "streambuf_operators.h"
#include "player.h"
#include "vector_set.h"

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
t_adventure_object::t_adventure_object()
{
	m_global_id = k_invalid_global_id;
	m_map = 0;
	m_map_id = -1;
	m_floating = false;
	m_removal_count = 0;
	m_event_recordable = true;
}

t_adventure_object::~t_adventure_object()
{
	stop_owner_sound(this); // end any adventure sound this has running
}

t_adventure_object::t_clone_ptr t_adventure_object::clone() const
{
	return t_clone_ptr();
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                                   t_direction direction, t_adventure_frame* frame )
{
}

// -------------------------------------------------------------------
// query for event recordable flag
// -------------------------------------------------------------------
bool t_adventure_object::is_event_recordable() const
{
	return m_event_recordable;
}

// -------------------------------------------------------------------
// set the event recordable flag
// -------------------------------------------------------------------
void t_adventure_object::set_event_recordable(bool event_recordable)
{
	m_event_recordable = event_recordable;
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
std::string t_adventure_object::get_balloon_help() const
{
	return get_name();
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::initialize( t_adventure_map& map )
{
}

bool t_adventure_object::is_bridge() const
{
	return false;
}

bool t_adventure_object::is_ramp() const
{
	return false;
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::place( t_adventure_map& map, t_adv_map_point const& point )
{
	assert( m_map == 0 );
	
	update_state();

	m_floating	= false;
	m_map		= &map;
	m_location	= point;

	if ( m_global_id == k_invalid_global_id ) 
	{
		m_global_id = map.get_new_global_adventure_id();
	} 
	else 
	{
		// We must have been read from a file and already have a global id assigned
	}

	m_map_id = map.place_object( this, point );

	t_adventure_object_memory_cache_refrence::set_adventure_manager_for_memory_cache( m_map );

	t_player * owner = get_owner();
	if ( owner != 0 )
	{
		int team_num = owner->get_team();

		t_visibility_set changed_tile_points;

		map.increase_visibility(
			team_num,
			m_location,
			get_footprint().get_size(),
			get_scouting_range(),
			get_attack_range(),
			get_anti_stealth_level(),
			changed_tile_points );

		if ( !changed_tile_points.empty() )
		{
			map.on_visibility_changed(
				team_num,
				m_location.level,
				changed_tile_points );
		}
	}
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
bool t_adventure_object::preplacement( t_adventure_map& map, int pass )
{
	return false;
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::destroy()
{
	if (m_map != 0 && m_map_id >= 0)
		m_map->remove_object( this );
}

// -------------------------------------------------------------------
// Called from ~t_adventure_map; last chance to clean up
// -------------------------------------------------------------------
void t_adventure_object::on_adventure_map_destruction()
{

}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::on_removed() 
{
	assert( m_map_id >= 0 );

	m_map_id = -1;

	t_player * owner = get_owner();
	if ( owner != 0 )
	{
		assert( m_map != 0 );

		int team_num = owner->get_team();

		t_visibility_set changed_tile_points;

		m_map->remove_visibility(
			team_num,
			m_location,
			get_footprint().get_size(),
			get_scouting_range(),
			changed_tile_points );

		if ( !changed_tile_points.empty() )
		{
			m_map->on_visibility_changed(
				team_num,
				m_location.level,
				changed_tile_points );
		}
	}
}


// --------------------------------------------------------
// scouting range for adventure objects
// --------------------------------------------------------
int t_adventure_object::get_scouting_range() const
{
	return 0;
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
int t_adventure_object::get_version() const
{
	return 0;
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::on_move_begin()
{
//	if (m_map == 0)
	if (!is_on_map())
		return;
	if (m_removal_count++ > 0)
		return;
	t_map_renderer::on_moving_adv_object( *m_map, m_map_id );
	t_mini_map_renderer::on_moving_adv_object( *m_map, m_map_id );
	float_object();
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::on_move_end()
{
//	if ( (m_map == 0) || (m_removal_count == 0) ) 
	if ( !is_on_map() || (m_removal_count == 0) ) 
		return;
	if (--m_removal_count > 0)
		return;
	sink_object();
	t_map_renderer::on_adv_object_moved( *m_map, m_map_id );
	t_mini_map_renderer::on_adv_object_moved( *m_map, m_map_id );
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::move( t_adv_map_point const& point )
{
	t_adv_map_point old_location = m_location;

	if (point == old_location)
		return;

	on_move_begin();

	m_location = point;

	t_player * owner = get_owner();
	if ( owner != 0 )
	{
		assert( m_map != 0 );

		int team_num = owner->get_team();
		t_map_point_2d footprint_size = get_footprint().get_size();

		t_visibility_set changed_tile_points;

		m_map->remove_visibility(
			team_num,
			old_location,
			footprint_size,
			get_scouting_range(),
			changed_tile_points );

		if ( m_location.level != old_location.level && !changed_tile_points.empty() )
		{
			m_map->on_visibility_changed(
				team_num,
				old_location.level,
				changed_tile_points );
			changed_tile_points.clear();
		}

		m_map->increase_visibility( 
			team_num,
			m_location,
			footprint_size,
			get_scouting_range(),
			get_attack_range(),
			get_anti_stealth_level(),
			changed_tile_points );

		if ( !changed_tile_points.empty() )
		{
			m_map->on_visibility_changed(
				team_num,
				m_location.level,
				changed_tile_points );
		}
	}

	on_move_end();
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
bool t_adventure_object::is_triggered_by( t_creature_array const& army ) const
{
	return true;
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
bool t_adventure_object::blocks_army( t_creature_array const&			 army,
									  t_path_search_type path_type ) const
{
	return false;
}


// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_adventure_object::left_double_click( t_mouse_event const& event, 
							                t_adventure_frame* adventure_frame )
{
}

void t_adventure_object::right_click( t_mouse_event const& event,
		                              t_adventure_frame* adventure_frame )
{
}


void t_adventure_object::update_state()
{
}

void t_adventure_object::preprocess_new_day()
{
}

void t_adventure_object::process_new_day()
{
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::get_trigger_cell( t_adv_map_point & result ) const
{
	t_footprint const&			footprint = get_footprint();
	t_map_point_2d				offset;
	t_map_point_2d				footprint_size = footprint.get_size();

	// look at each point in footprint, looking for trigger cells.
	for (offset.row = 0; offset.row < footprint_size.row; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size.column; offset.column++)
		{
			if (footprint.is_trigger_cell( offset ))
			{
				result = m_location + offset;
				return true;
			}
		}
	}
	return false;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// "Returns" a list of all nearby cells which can reach a trigger edge 
// by calling scanner on each one. If the third argument is set to 
// true, the scan will stop & return true
// -------------------------------------------------------------------------------
bool t_adventure_object::scan_adjacent_spaces( t_scan_adjacent_space_handler scanner, 
											   t_adv_map_point & stopping_point ) const
{
	if (m_map == 0)
		return false;

	t_footprint const& footprint =	get_footprint();
	t_adv_map_point					trigger_cell;
	t_adv_map_point					point;
	t_adv_map_point					center;
	t_map_point_2d					offset;
	t_map_point_2d					footprint_size = footprint.get_size();
	t_direction						direction;
	t_map_point_2d					direction_offset;
	t_map_point_2d					sub_offset;

	// look at each point in footprint, looking for trigger cells.
	for (offset.row = 0; offset.row < footprint_size.row; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size.column; offset.column++)
		{
			if (!footprint.is_trigger_cell( offset ))
				continue;

			trigger_cell = m_location + offset;

			// move off the trigger in a given direction.
			for (direction = t_direction(0); direction < k_direction_count; 
				 enum_incr( direction ))
			{
				direction_offset = get_direction_offset( direction );

				// find the new point.
				point = trigger_cell + direction_offset;

				if ( !m_map->is_valid( point ) )
					continue;

				// If, by going this way we hit a ramp, go onto the ramp
				if ( m_map->is_ramp_open( m_location + offset, direction ) )
					point.on_bridge = !point.on_bridge;

				// find out if by moving in the opposite direction, we cross
				// the trigger edge.
				if (!m_map->get_trigger_object( point, t_direction( (direction + 4) & 7 ), 0 ))
					continue;

				bool stop = false;

				scanner( point, trigger_cell, stop );

				if ( stop )
				{
					stopping_point = point;
					return true;
				}
			}
		}
	}

	return false;
}

namespace
{
	// Data for find_adjacent_space_helper. 
	struct t_find_adjacent_space_helper_data
	{
		t_adventure_map * map;
		t_creature_array const * army;
		bool find_ocean_space;
		t_adventure_object::t_find_space_filter_handler filter_function;
	};
}

// -------------------------------------------------------------------------------
// Scanner function for find_adjacent_space
// -------------------------------------------------------------------------------
static void find_adjacent_space_helper( t_adv_map_point const & point, 
									    bool & stop, 
										t_find_adjacent_space_helper_data & data )
{
	// find out if this region is blocked.
	if ( !data.map->is_open( point, *data.army ) )
		return;

	// See if this terrain is appropriate for this army
	if ( ( !point.on_bridge && data.map->is_ocean( point ) ) != data.find_ocean_space )
		return;

	if ( data.map->get_adv_tile( point ).has_ramp() && !point.on_bridge )
		return; // Can't get onto the ramp and don't want to end up under it

	bool filter = false;
	data.filter_function( point, filter );
	if ( filter )
		return;
	
	stop = true;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::find_adjacent_space( t_adv_map_point & result, 
											  t_creature_array const& army, 
											  t_find_space_filter_handler filter_function /* = t_find_space_filter_handler() */ ) const
{
	return find_adjacent_space( result, army, army.is_boat(), filter_function );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::find_adjacent_space( t_adv_map_point & result, 
											  t_creature_array const& army,
											  bool find_ocean_space,
											  t_find_space_filter_handler filter_function /* = t_find_space_filter_handler() */ ) const
{
	t_find_adjacent_space_helper_data data;

	data.map = m_map;
	data.army = &army;
	data.find_ocean_space = find_ocean_space;
	data.filter_function = filter_function;

	t_handler_3< t_adv_map_point const &, bool &, t_find_adjacent_space_helper_data & > sub_handler_1 =
		function_3_handler( find_adjacent_space_helper );

	t_handler_2< t_adv_map_point const &, bool & > sub_handler_2 = 
		add_3rd_argument< t_find_adjacent_space_helper_data & >( sub_handler_1, data );

	// Can't get this to compile.... I think we may be overstraining the compiler's
	// template handling at this point
	// t_scan_adjacent_space_handler scanner = discard_2nd_argument< t_adv_map_point const &, t_adv_map_point const &, bool & >( sub_handler_2 );
	t_scan_adjacent_space_handler scanner = new t_discard_2nd_handler_3< t_adv_map_point const &, t_adv_map_point const &, bool & >( sub_handler_2 );

	return scan_adjacent_spaces( scanner, result );
}


namespace
{
	// Class to scan points near the current object until a good place to put an
	// army is found
	class t_find_nearby_space_scanner
	{
		public:
			bool find_nearby_space( t_adv_map_point & result, t_adventure_path_finder & pathfinder, t_creature_array const& army );

		private:
			void pathfinder_callback( t_adventure_path_point const & point, bool & stop );

			t_creature_array const *	m_army;
			t_adv_map_point	*			m_best_point;
			bool						m_found_point;
			t_adventure_map *			m_map;
	};

	bool t_find_nearby_space_scanner::find_nearby_space( t_adv_map_point & result, t_adventure_path_finder & pathfinder, t_creature_array const& army )
	{
		m_map = pathfinder.get_map();
		m_army = &army;
		m_found_point = false;
		m_best_point = &result;

		pathfinder.handler_based_search( bound_handler( *this, &t_find_nearby_space_scanner::pathfinder_callback ) );

		return m_found_point;
	}

	void t_find_nearby_space_scanner::pathfinder_callback( t_adventure_path_point const & point, bool & stop )
	{
		stop = false;

		if ( !m_map->is_open( point, *m_army ) )
			return;

		// See if this terrain is appropriate for this army
		if ( ( !point.on_bridge && m_map->is_ocean( point ) ) != m_army->is_boat())
			return;

		if ( m_map->get_adv_tile( point ).has_ramp() && !point.on_bridge )
			return; // Can't get onto the ramp and don't want to end up under it
	
		// Prefer points with no enemy adjacent, but take such points over not finding 
		// anything at all. Always return the CLOSEST point of whatever type
		if ( !point.actual_enemy_adjacent || !m_found_point )
		{
			m_found_point = true;
			*m_best_point = point;

			if ( !point.actual_enemy_adjacent )
				stop = true;
		}
	}
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// this method does an expanding search to find a legal space near the object
// -------------------------------------------------------------------------------
bool t_adventure_object::find_nearby_space( t_adv_map_point & result, t_creature_array const& army ) const
{
	if (m_map == 0)
		return false;

	int i;
	t_adventure_path_finder pathfinder( *m_map );

	// Note: daily movement doesn't actually matter much
	t_pathfinding_subject_ptr subject_ptr = new t_pathfinding_subject( m_map, army.get_owner_number(), 1000 );

	// COPY -- yes, copy -- army into the pathfinding subject to make tests work correctly
	// But we don't want to actually modify army
	for ( i = 0; i < t_creature_array::k_size; i++ )
	{
		subject_ptr->add( army[i].clone() );
	}

	if ( !start_object_adjacent_pathfind( pathfinder, *this, *subject_ptr, k_path_search_find_nearby_space,
										  bound_handler( *subject_ptr, &t_pathfinding_subject::set_position ) ) )
	{
		return false;
	}

	t_find_nearby_space_scanner scanner;

	return scanner.find_nearby_space( result, pathfinder, army );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_bitmap_cursor const& t_adventure_object::get_cursor( t_adventure_map_window const& window,
		                                             t_army const* army ) const
{
	return window.get_activate_cursor();
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_adventure_map_window* t_adventure_object::get_map_window() const
{
	if (m_map == 0)
		return 0;
	return m_map->get_map_window();
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_adventure_frame* t_adventure_object::get_adventure_frame() const
{
	t_adventure_map_window* window = get_map_window();

	if (window == 0)
		return 0;
	return window->get_frame();
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_adventure_ai const* t_adventure_object::get_ai() const
{
	return &m_map->get_ai();
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_adventure_map* t_adventure_object::get_map() const
{
	return m_map;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::float_object()
{
	if (m_floating)
		return;
	m_floating = true;
	m_map->float_object( m_map_id );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::sink_object()
{
	if (!m_floating)
		return;
	m_floating = false;
	m_map->sink_object( m_map_id );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles).  Do not call
// this function from your derived class' read_from_map() implementation.  This
// implementation will just eat the default version number which should always
// be zero.
// -------------------------------------------------------------------------------
bool t_adventure_object::read_from_map( std::streambuf & buffer, t_progress_handler * handler )
{
	int version = get< t_uint16 >( buffer );
	return version == 0;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::is_removable() const
{
	return false;
}

bool t_adventure_object::uses_bridge_heights() const
{
	return m_location.on_bridge;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::mark_visibility(
	int						level,
	t_visibility_overlay &	visibility_overlay ) const
{
	assert( get_owner() != 0 );

	if ( !is_on_map() || m_location.level != level )
		return;
	assert( m_map != 0 );

	int radius = get_scouting_range();
	if ( radius == 0 )
		return;

	t_skill_mastery scouting = get_anti_stealth_level();

	int half_cell_radius_sq = radius * radius * 4;

	// Compute the center point in half cell coordinates
	t_map_point_2d footprint_size = get_footprint().get_size();
	t_map_point_2d object_center = t_map_point_2d( m_location ) * 2 + footprint_size;
	t_map_rect_2d rect = get_visibility_rect( object_center, radius, m_map->get_size() );

	t_map_rect_2d const & overlay_extent = visibility_overlay.get_extent();
	if ( intersect( rect, overlay_extent ) )
	{
		rect = intersection( rect, overlay_extent );

		t_map_point_2d tile_point;
		for (	tile_point.row = rect.top_left.row;
				tile_point.row < rect.bottom_right.row;
				++tile_point.row )
		{
			int first_col = m_map->get_row_start( tile_point.row );
			if ( first_col < rect.top_left.column )
				first_col = rect.top_left.column;

			int last_col = m_map->get_row_end( tile_point.row );
			if ( last_col > rect.bottom_right.column )
				last_col = rect.bottom_right.column;

			for (	tile_point.column = first_col;
					tile_point.column < last_col;
					++tile_point.column )
			{
				int half_cell_delta_row = tile_point.row * 2 + 1 - object_center.row;
				int half_cell_delta_col = tile_point.column * 2 + 1 - object_center.column;
				int half_cell_distance_sq =
					half_cell_delta_row * half_cell_delta_row + half_cell_delta_col * half_cell_delta_col;
				if ( half_cell_distance_sq > half_cell_radius_sq )
					continue;

				t_visibility_overlay::reference visibility_data = visibility_overlay[ tile_point ];
				if ( !visibility_data.is_visible() || visibility_data.get_anti_stealth() < scouting )
					visibility_data = t_tile_visibility_data( scouting );
			}
		}
	}

	// Mark the tiles within the 'attack' range
	radius = get_attack_range();
	if ( radius == 0 )
		return;

	// Bump the scouting level for the 'attack' range
	if ( scouting < k_mastery_grand_master )
		enum_incr( scouting );

	// Compute the footprint rect
	rect = t_map_rect_2d( m_location, footprint_size );
	rect.top_left -= t_map_point_2d( radius, radius );
	if ( rect.top_left.row < 0 )
		rect.top_left.row = 0;
	if ( rect.top_left.column < 0 )
		rect.top_left.column = 0;
	rect.bottom_right += t_map_point_2d( radius, radius );
	int map_size = m_map->get_size();
	if ( rect.bottom_right.row > map_size )
		rect.bottom_right.row = map_size;
	if ( rect.bottom_right.column > map_size )
		rect.bottom_right.column = map_size;

	if ( intersect( rect, overlay_extent ) )
	{
		rect = intersection( rect, overlay_extent );

		t_map_point_2d tile_point;
		for (	tile_point.row = rect.top_left.row;
				tile_point.row < rect.bottom_right.row;
				++tile_point.row )
		{
			int first_col = m_map->get_row_start( tile_point.row );
			if ( first_col < rect.top_left.column )
				first_col = rect.top_left.column;

			int last_col = m_map->get_row_end( tile_point.row );
			if ( last_col > rect.bottom_right.column )
				last_col = rect.bottom_right.column;

			for (	tile_point.column = first_col;
					tile_point.column < last_col;
					++tile_point.column )
			{
				t_visibility_overlay::reference visibility_data = visibility_overlay[ tile_point ];
				if ( !visibility_data.is_visible() || visibility_data.get_anti_stealth() < scouting )
					visibility_data = t_tile_visibility_data( scouting );
			}
		}
	}
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::on_owner_changed( int new_owner, int old_owner )
{
	assert( new_owner != old_owner );

	if ( is_on_map()  )
	{
		t_adventure_map * map_ptr = get_map();
		assert( map_ptr != 0 );

		int old_team_num;
		if ( old_owner < 0 )
			old_team_num = -1;
		else
			old_team_num = map_ptr->get_player( old_owner ).get_team();

		int new_team_num;
		if ( new_owner < 0 )
			new_team_num = -1;
		else
			new_team_num = map_ptr->get_player( new_owner ).get_team();

		if ( new_team_num != old_team_num )
		{
			t_map_point_2d footprint_size = get_footprint().get_size();
			int scouting_range = get_scouting_range();
			int attack_range = get_attack_range();
			t_skill_mastery anti_stealth_level = get_anti_stealth_level();
	
			if ( old_team_num >= 0 )
			{
				t_visibility_set changed_tile_points;
				map_ptr->remove_visibility(
					old_team_num,
					m_location,
					footprint_size,
					scouting_range,
					changed_tile_points );

				if ( !changed_tile_points.empty() )
				{
					map_ptr->on_visibility_changed(
						old_team_num,
						m_location.level,
						changed_tile_points );
				}
			}

			if ( new_team_num >= 0 )
			{
				t_visibility_set changed_tile_points;
				map_ptr->increase_visibility(
					new_team_num,
					m_location,
					footprint_size,
					scouting_range,
					attack_range,
					anti_stealth_level,
					changed_tile_points );

				if ( !changed_tile_points.empty() )
				{
					map_ptr->on_visibility_changed(
						new_team_num,
						m_location.level,
						changed_tile_points );
				}
			}
		}
	}
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::on_scouting_range_changed( int new_range, int old_range )
{
	assert( new_range != old_range );

	if ( is_on_map()  )
	{
		t_player * owner = get_owner();
		if ( owner != 0 )
		{
			t_adventure_map * map_ptr = get_map();
			assert( map_ptr != 0 );

			int team_num = owner->get_team();
			t_map_point_2d footprint_size = get_footprint().get_size();

			t_visibility_set changed_tile_points;
			if ( new_range < old_range )
			{
				map_ptr->remove_visibility(
					team_num,
					m_location,
					footprint_size,
					old_range,
					changed_tile_points );
			}
			else
			{
				map_ptr->increase_visibility(
					team_num,
					m_location,
					footprint_size,
					new_range,
					get_attack_range(),
					get_anti_stealth_level(),
					changed_tile_points );
			}

			if ( !changed_tile_points.empty() )
			{
				map_ptr->on_visibility_changed(
					team_num,
					m_location.level,
					changed_tile_points );
			}
		}
	}
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::process_timed_events( t_adventure_map& map, bool start_of_day )
{
	return false;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::process_continuous_events( t_adventure_map& map )
{
	return false;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::process_triggerable_events( t_adventure_map& map, std::string const& name )
{
	return false;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::is_under_fog_of_war( int team_num ) const
{
	return		is_on_map()
			&&	::is_under_fog_of_war( *m_map, team_num, m_location, get_footprint().get_size() );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::is_under_shroud( int team_num ) const
{
	return		is_on_map()
			&&	::is_under_shroud( *m_map, team_num, m_location, get_footprint().get_size() );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::hidden_by_fog_of_war( int team_num ) const
{
	return team_num >= 0 && !is_visible_to( team_num );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::hidden_by_shroud( int team_num ) const
{
	return		get_enable_shroud()
			&&	team_num >= 0
			&&	is_under_shroud( team_num );
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// virtual function used by the pathfinder to determine if an object
// should place additonal points into the path search
// -------------------------------------------------------------------------------
void t_adventure_object::pathing_destination_query( t_adventure_path_point const& source, 
													t_adventure_path_finder & path_finder ) const
{
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_adventure_object*	t_adventure_object::get_adventure_object()
{
	return this;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::on_begin_turn()
{
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::on_end_turn()
{
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_adventure_object::t_icon_type t_adventure_object::get_icon_type() const
{
	return k_adv_object_icon_none;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
void t_adventure_object::on_combat_end( t_army* attacker, t_combat_result result,
										t_creature_array* losses, t_adventure_frame* frame )
{
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
bool t_adventure_object::trigger_event(t_army& army)
{
	return false;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
int t_adventure_object::get_attack_range() const
{
	return 0;
}

// -------------------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------------------
t_skill_mastery t_adventure_object::get_anti_stealth_level() const
{
	return k_mastery_none;
}

// -------------------------------------------------------------------------------
// get information given to local player by scouting level.
// -------------------------------------------------------------------------------
t_skill_mastery t_adventure_object::get_information_level() const
{
	t_adventure_map*	map = get_map();
	t_player const&		current_player = map->get_player();
	t_adv_map_point		trigger_point;

	if (!get_trigger_cell( trigger_point ))
		return k_mastery_none;

	t_adventure_tile const& tile = map->get_adv_tile( trigger_point );
	int						team = current_player.get_team();

	if (tile.get_visibility( team ) < k_tile_visible)
		return k_mastery_none;

    if (same_team( &current_player, get_owner() )) 
		return k_mastery_grand_master;

    // Get position of selected army.
	t_army_array const*	army_list = current_player.get_armies();
	t_skill_mastery		result = k_mastery_none;
	t_skill_mastery		scouting;

    /*
    ** Go through all friendly armies, and see if hero is in
    ** scouting range for this army.  If so, get the hero
    ** with the highest scouting skill level.
    */
	int i, j;

    for (i = 0; i < army_list->size(); i++)
    {
		t_army& army = *(*army_list)[i];

        // Any heroes?
   	    t_level_map_point_2d	army_position = army.get_position();
        
		if (army_position.level != trigger_point.level)
			continue;
        /*
        ** Get heroes - find the hero with the highest skill.
        */
        double	army_distance = distance ( army_position - trigger_point );

        for (j = 0; j < t_creature_array::k_size; j++)
        {
	        t_hero * hero = army[j].get_hero();

            // Hero or not?        
            if (hero == 0 || hero->is_dead())
			    continue;

            // Within scouting range?
            if (hero->compute_scouting_range() < army_distance)
                continue;
           
            scouting = hero->get_skill(k_skill_scouting);

			// The hero effectivly has master scouting if the "visions" spell is active
			if ( scouting < k_mastery_master && hero->is_spell_active( k_spell_visions ) )
				scouting = k_mastery_master;

           if ( result < scouting)
			   result = scouting;
        }
    }
	return result;
}
